using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Quartz;
using StackExchange.Redis;
using Zhaoxi.Zhihu.Core.AppUserAggregate.Interfaces;
using Zhaoxi.Zhihu.Core.AppUserAggregate.Service;
using Zhaoxi.Zhihu.Core.Interface;
using Zhaoxi.Zhihu.Core.Service;
using Zhaoxi.Zhihu.Infrastructure.Identity;
using Zhaoxi.Zhihu.Infrastructure.Interceptors;
using Zhaoxi.Zhihu.Infrastructure.Repositories;
using Zhaoxi.Zhihu.SharedKerel.Repositoy;
using Zhaoxi.Zhihu.UseCases.Common.Interface;
using Zhaoxi.Zhihu.UseCases.Common.Queries.Interfaces;
using Zhaoxi.Zhihu.UseCases.Questions.Job;

namespace Zhaoxi.Zhihu.Infrastructure;

public static class DependencyInjection
{
    public static IServiceCollection AddInfrastructureServices(this IServiceCollection services,
        IConfiguration configuration)
    {
        ConfigureEfCore(services, configuration);

        ConfigureIdentity(services, configuration);
        
        ConfigureQuartz(services, configuration);
        
        return services;
    }

    private static void ConfigureEfCore(IServiceCollection services, IConfiguration configuration)
    {
        var connectionString = configuration.GetConnectionString("DefaultConnection");


        //注入拦截器添加时间信息
        services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
        services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();

        services.AddDbContext<AppDbContext>((sp, options) =>
        {
            options.AddInterceptors(sp.GetService<ISaveChangesInterceptor>());

            options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
        });

        services.AddScoped<AppDbInitializer>();

        services.AddScoped(typeof(IReadRepository<>), typeof(EfReadRepository<>));

        services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
        services.AddScoped<IDaraQueryService, DataQueryService>();
        services.AddScoped<IAnswerRepositoy, AnswerRepositoy>();
        services.AddScoped<IAppUserService, AppUserService>();

    }

    private static void ConfigureIdentity(IServiceCollection services, IConfiguration configuration)
    {
        services
            .AddIdentityCore<IdentityUser>(options =>
            {
                options.User.RequireUniqueEmail = true;
                options.Password.RequiredLength = 8;
            })
            .AddEntityFrameworkStores<AppDbContext>();

        services.AddScoped<IIdentityService,IdentityService>();
        
        //从配置文件读取，注入到容器中
        var configureationSection = configuration.GetSection("JwtSettings");
        var JwtSettings = configureationSection.Get<JwtSettings>();
        if (JwtSettings is null) throw new NullReferenceException(nameof(JwtSettings));
        services.Configure<JwtSettings>(configureationSection);

        ConfigureAuthentication(services, JwtSettings);
        ConfigureRedis(services, configuration);
    }

    public static void ConfigureAuthentication(IServiceCollection services, JwtSettings jwtSettings)
    {
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ClockSkew = TimeSpan.Zero,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = jwtSettings.Issuer,
                    ValidAudience = jwtSettings.Audience,
                    IssuerSigningKey = new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(jwtSettings.Secret))
                };
            });
    }

    private static void ConfigureQuartz(IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<QuartzOptions>(configuration.GetSection("Quartz"));
        services.AddTransient<UpdateQuestionViewCountsJob>();
        services.AddQuartz(configurator => { configurator.CreateUpdateQuestionViewCountJobSchedule(); });
        services.AddQuartzHostedService(opt =>
        {
            opt.WaitForJobsToComplete = true;
            opt.StartDelay=TimeSpan.FromMinutes(5);
        });
    }

    private static void ConfigureRedis(IServiceCollection services, IConfiguration configuration)
    {
        var redisConn = configuration. GetConnectionString("RedisConnection");
        if (redisConn != null)
        {
            services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect (redisConn));
        }
            
    }
}